2 * Adium is the legal property of its developers, whose names are listed in the copyright file included
3 * with this source distribution.
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #import <Adium/AIContactControllerProtocol.h>
18 #import "AIMessageTabViewItem.h"
19 #import "AIMessageViewController.h"
20 #import "AIMessageWindowController.h"
21 #import "AIContactController.h"
22 #import "AIDualWindowInterfacePlugin.h"
23 #import <AIUtilities/AIImageAdditions.h>
24 #import <Adium/AIChat.h>
25 #import <Adium/AIListContact.h>
26 #import <Adium/AIServiceIcons.h>
27 #import <Adium/AIStatusIcons.h>
28 #import <Adium/AIPreferenceControllerProtocol.h>
30 #define BACK_CELL_LEFT_INDENT -1
31 #define BACK_CELL_RIGHT_INDENT 3
32 #define LABEL_SIDE_PAD 0
34 @interface AIMessageTabViewItem (PRIVATE)
35 - (id)initWithMessageView:(AIMessageViewController *)inMessageView;
36 - (void)drawLabel:(BOOL)shouldTruncateLabel inRect:(NSRect)labelRect;
37 - (NSSize)sizeOfLabel:(BOOL)computeMin;
38 - (NSAttributedString *)attributedLabelStringWithColor:(NSColor *)textColor;
39 - (void)chatParticipatingListObjectsChanged:(NSNotification *)notification;
40 - (void)chatStatusChanged:(NSNotification *)notification;
43 @implementation AIMessageTabViewItem
46 + (AIMessageTabViewItem *)messageTabWithView:(AIMessageViewController *)inMessageView
48 return [[[self alloc] initWithMessageView:inMessageView] autorelease];
52 - (id)initWithMessageView:(AIMessageViewController *)inMessageViewController
54 /* XXX - Warning! Setting self as the identifier also means that we are retaining ourselves! Something _must_
55 * break us out of this infinite loop. This happens in -[AIMesageWindowController removeTabViewItem:silent:].
57 if ((self = [super initWithIdentifier:self])) {
58 messageViewController = [inMessageViewController retain];
59 adium = [AIObject sharedAdiumInstance];
60 windowController = nil;
62 //Configure ourself for the message view
63 [[adium notificationCenter] addObserver:self selector:@selector(chatStatusChanged:)
64 name:Chat_StatusChanged
65 object:[messageViewController chat]];
66 [[adium notificationCenter] addObserver:self selector:@selector(chatAttributesChanged:)
67 name:Chat_AttributesChanged
68 object:[messageViewController chat]];
69 [[adium notificationCenter] addObserver:self selector:@selector(chatParticipatingListObjectsChanged:)
70 name:Chat_ParticipatingListObjectsChanged
71 object:[messageViewController chat]];
72 [self chatStatusChanged:nil];
73 [self chatParticipatingListObjectsChanged:nil];
76 [self setView:[messageViewController view]];
84 [[adium notificationCenter] removeObserver:self];
86 [tabViewItemImage release]; tabViewItemImage = nil;
87 [messageViewController release]; messageViewController = nil;
92 //Access to our message view controller
93 - (AIMessageViewController *)messageViewController
95 return messageViewController;
101 return [messageViewController chat];
104 //Our containing window
105 - (void)setWindowController:(AIMessageWindowController *)inWindowController{
106 if (inWindowController != windowController) {
107 [messageViewController messageViewWillLeaveWindowController:windowController];
109 windowController = inWindowController;
111 [messageViewController messageViewAddedToWindowController:windowController];
115 - (AIMessageWindowController *)windowController{
116 return windowController;
119 //Message View Delegate ----------------------------------------------------------------------
120 #pragma mark Message View Delegate
123 * @brief The list objects participating in our chat changed
125 - (void)chatParticipatingListObjectsChanged:(NSNotification *)notification
127 AIListObject *listObject;
129 //Remove the old observer
130 [[adium notificationCenter] removeObserver:self name:ListObject_AttributesChanged object:nil];
132 //If there is a single list object for this chat, observe its attribute changes
133 if ((listObject = [messageViewController listObject])) {
134 [[adium notificationCenter] addObserver:self
135 selector:@selector(listObjectAttributesChanged:)
136 name:ListObject_AttributesChanged
143 - (void)chatStatusChanged:(NSNotification *)notification
145 NSArray *keys = [[notification userInfo] objectForKey:@"Keys"];
147 //If the display name changed, we resize the tabs
148 if (notification == nil || [keys containsObject:@"DisplayName"]) {
149 id delegate = [[self tabView] delegate];
150 //[delegate resizeTabForTabViewItem:self];
152 /* This should really be looked at and possibly a better method found.
153 * This works and causes an automatic update to each open tab. But it feels like a hack.
154 * There is probably a more elegant method. Something like [[[self tabView] delegate] redraw];
155 * I guess that's what this causes to happen, but the indirectness bugs me. It's obviously not the best solution,
156 * but good enough for now.
158 [delegate tabViewDidChangeNumberOfTabViewItems:[self tabView]];
159 } else if ([keys containsObject:@"UnviewedContent"]) {
160 [self setValue:nil forKeyPath:@"objectCount"];
164 - (void)chatAttributesChanged:(NSNotification *)notification
166 NSArray *keys = [[notification userInfo] objectForKey:@"Keys"];
168 //Redraw if the icon has changed
169 if (keys == nil || [keys containsObject:@"Tab State Icon"]) {
170 [[self windowController] updateIconForTabViewItem:self];
171 [self setValue:nil forKeyPath:@"icon"];
175 - (void)listObjectAttributesChanged:(NSNotification *)notification
177 AIListObject *listObject = [notification object];
178 AIListContact *messageViewContact = [messageViewController listObject]; //Is the contact in the message view part of a metacontact?
180 if (!listObject || (listObject == messageViewContact) || listObject == [messageViewContact parentContact]) {
181 NSSet *keys = [[notification userInfo] objectForKey:@"Keys"];
183 //Redraw if the icon has changed
184 if (!keys || [keys containsObject:@"Tab Status Icon"]) {
185 [[self windowController] updateIconForTabViewItem:self];
186 [self setValue:nil forKeyPath:@"icon"];
189 //If the list object's display name changed, we resize the tabs
190 if (!keys || [keys containsObject:@"Display Name"]) {
191 [self setLabel:[self label]];
196 //Interface Container ----------------------------------------------------------------------
197 #pragma mark Interface Container
199 //Make this container active
200 - (void)makeActive:(id)sender
202 NSTabView *tabView = [self tabView];
203 NSWindow *window = [tabView window];
205 if ([tabView selectedTabViewItem] != self) {
206 [tabView selectTabViewItem:self]; //Select our tab
209 if (![window isKeyWindow] || ![window isVisible]) {
210 [window makeKeyAndOrderFront:nil]; //Bring our window to the front
214 //Close this container
215 - (void)close:(id)sender
217 [[self tabView] removeTabViewItem:self];
222 //Tab view item ----------------------------------------------------------------------
223 #pragma mark Tab view item
225 //Called when our tab is selected
226 - (void)tabViewItemWasSelected
228 //Ensure our entry view is first responder
229 [messageViewController makeTextEntryViewFirstResponder];
235 return ([[messageViewController chat] displayName]);
238 - (void)setIcon:(NSImage *)newIcon
240 //method does nothing; force the tab bindings to reload -icon
243 //Return the icon to be used for our tabs. State gets first priority, then status.
246 NSImage *image = [self stateIcon];
248 //Multi-user chats won't have status icons
249 if (!image && ![messageViewController userListVisible]) image = [self statusIcon];
251 if (!image) image = [AIStatusIcons statusIconForUnknownStatusWithIconType:AIStatusIconTab direction:AIIconNormal];
256 //Status icon is the status of this contact (away, idle, online, stranger)
257 - (NSImage *)statusIcon
259 return [[[messageViewController chat] listObject] displayArrayObjectForKey:@"Tab Status Icon"];
262 //State icon is the state of the contact (Typing, unviewed content)
263 - (NSImage *)stateIcon
265 return [[messageViewController chat] displayArrayObjectForKey:@"Tab State Icon"];
270 return tabViewItemImage;
273 - (void)setLargeImage:(NSImage *)inImage
278 - (NSImage *)largeImage
280 return [[[self chat] chatImage] imageByScalingToSize:NSMakeSize(48,48)];
283 //bindings methods for PSMTabBarControl
285 - (void)setObjectCount:(NSNumber *)number
287 //method does nothing; force the tab bindings to reload -objectCount
292 //return 0 to disable the badge
293 return ([[[adium preferenceController] preferenceForKey:KEY_TABBAR_SHOW_UNREAD_COUNT group:PREF_GROUP_DUAL_WINDOW_INTERFACE] boolValue] ?
294 [[self chat] unviewedContentCount] : 0);
297 - (void)tabViewDidChangeVisibility
299 [[self messageViewController] tabViewDidChangeVisibility];